
/* GCSx
** RUNDATA.H
**
** Runtime data support- stacks, variables, hashes, arrays
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#ifndef __GCSx_RUNDATA_H_
#define __GCSx_RUNDATA_H_

// Subtypes for entitys/objects
enum {
    // Type unknown/any (within entity domain or object domain only)
    SUB_NONE = 0,
    // Actual built-in types go here
    SUB_SPRITE = 1,
    SUB_SCENE = 2,
    
    SUB_ENTITY = 255,
    // The start of user script type ids
    SUB_ENTITY_FIRST = 256
};

// Stack entry/data types
enum {
    STACK_UNDEF = 0,

    // Basic types match up with opcode modes
    STACK_INT = 1, // i
    STACK_FLOAT = 2, // f
    STACK_STRING = 3, // p(string), owned by us
    STACK_ARRAY = 5, // p(Array), reference counted
    STACK_HASH = 6, // p(Hash), reference counted
    STACK_ENTITY = 7, // p(Entity) subtype must be manually looked up if needed
    STACK_OBJECT = 8, // p(ObjectBase) subtype must be manually looked up if needed
    STACK_BASETYPE = 15, // bitmask/maximum
    
    STACK_INDIRECT = 16, // Added to 1-15, becomes p(RunData)
    STACK_REPLYPTR = 32, // p(StackEntry); added to 1-15

    STACK_CODEPTR = 48, // p(Uint32)
    STACK_RETURNPTR = 49, // p(StackEntry)

    STACK_TYPE_COUNT = 50
};

// Data stored in our data structures
// i)nteger, f)loat, p)ointer (various types)
union RunData {
    Sint32 i;
    BCfloat f;
    void* p;
};

// Built-in object types, base class
class ObjectBase {
public:
    // May be any number of members- based on object type
    RunData* members;
    int subtype;
    // 0 = destroy (counted manually for speed/simplicity)
    // As long as positive, object must NOT delete itself
    // unless game is unloading.
    int ref;
    // @TODO: way to handle member functions
};

// Reference-counted hashes/arrays
// @TODO: better method without storing RunData-sized items?
// do as manual dyn-array similar to stacks?
struct Array {
    int ref; // 0 = destroy
    int type;
    int subtype;
    std::vector<RunData> data;
};
struct Hash {
    int ref; // 0 = destroy
    int type;
    int subtype;
    // @TODO: be wary of deleting the keys before the entries/clearing
    // note that this is case-sensitive! (and will remain so)
    typedef hash_map<const char*, RunData, hash<const char*>, hash_eqstr> Data;
    Data data;
};

// Hash/array reference counting
// Decreases assume you are discarding your ptr
inline void refIncArray(Array* a) {
    ++a->ref;
}
inline void refDecArray(Array* a) {
    interpretAssert(a->ref > 0);
    if (--a->ref == 0) {
        // @TODO: delete strings/objects within array
        delete a;
    }
}
inline void refIncHash(Hash* h) {
    ++h->ref;
}
inline void refDecHash(Hash* h) {
    interpretAssert(h->ref > 0);
    if (--h->ref == 0) {
        // @TODO: delete strings/objects within hash
        delete h;
    }
}

// Also used for global variables, for consistency
struct StackEntry {
    RunData data;
    int type;
};

// Not OOP to streamline+optimize
struct Stack {
    StackEntry* data;
    StackEntry* top; // Points to one past top element
    StackEntry* allocSize; // Points to one past last allocated element
};

// Slower functions
void createStack(Stack* s);
void destroyStack(Stack* s);
void increaseStack(Stack* s);

// Ensure room for another push
inline void prepStack(Stack& s) {
    if (s.top == s.allocSize)
        increaseStack(&s);
}
inline void prepStack(Stack* s) {
    if (s->top == s->allocSize)
        increaseStack(s);
}

// Stack Entry dereferencing functions- assumes you're discarding or overwriting
// it afterwards so doesn't entirely/safely clean it for further use
// @TODO: just inline into deref* functions if we don't call them elsewhere
inline void seDerefString(StackEntry* s) {
    interpretAssert(s->type == STACK_STRING);
    delete (std::string*)s->data.p;
}
inline void seDerefArray(StackEntry* s) {
    interpretAssert(s->type == STACK_ARRAY);
    refDecArray((Array*)s->data.p);
}
inline void seDerefHash(StackEntry* s) {
    interpretAssert(s->type == STACK_HASH);
    refDecHash((Hash*)s->data.p);
}
// (seDerefEntity is part of Entity)
inline void seDerefObject(StackEntry* s) {
    interpretAssert(s->type == STACK_OBJECT);
    if (s->data.p) --(((ObjectBase*)s->data.p)->ref);
}

#endif
